package org.dromara.common.core.utils;

public class SnowflakeIdGenerator {
    // 起始时间戳，这里设置为 2020-01-01 00:00:00
    private final long startTimeStamp = 1577836800000L;

    // 数据中心 ID 所占位数
    private final long dataCenterIdBits = 5L;
    // 机器 ID 所占位数
    private final long machineIdBits = 5L;
    // 序列号所占位数
    private final long sequenceBits = 12L;

    // 数据中心 ID 最大值
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
    // 机器 ID 最大值
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    // 序列号最大值
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 机器 ID 向左移位数
    private final long machineIdShift = sequenceBits;
    // 数据中心 ID 向左移位数
    private final long dataCenterIdShift = sequenceBits + machineIdBits;
    // 时间戳向左移位数
    private final long timestampLeftShift = sequenceBits + machineIdBits + dataCenterIdBits;

    // 数据中心 ID
    private final long dataCenterId;
    // 机器 ID
    private final long machineId;
    // 序列号
    private long sequence = 0L;
    // 上一次生成 ID 的时间戳
    private long lastTimestamp = -1L;


    /**
     * 在 SnowflakeIdGenerator 类的构造函数 SnowflakeIdGenerator(long dataCenterId, long machineId) 中，SnowflakeIdGenerator(1, 1) 里的两个参数分别代表数据中心 ID（dataCenterId）和机器 ID（machineId），下面为你详细解释：
     * 数据中心 ID（dataCenterId）
     * 含义：在分布式系统中，为了更好地管理和划分资源，可能会将系统部署在多个不同的数据中心。数据中心 ID 用于唯一标识不同的数据中心。例如，一个大型企业可能在不同的城市有多个数据中心，每个数据中心就可以用一个唯一的 ID 来区分。
     * 取值范围：在 SnowflakeIdGenerator 类中，数据中心 ID 所占的位数由 dataCenterIdBits 决定，这里设置为 5 位，所以数据中心 ID 的取值范围是从 0 到 2^5 - 1，即 0 到 31。在 SnowflakeIdGenerator(1, 1) 中，第一个参数 1 表示当前使用的数据中心 ID 为 1。
     * 机器 ID（machineId）
     * 含义：在每个数据中心内部，可能会有多个服务器（机器）来处理业务。机器 ID 用于唯一标识同一个数据中心内的不同机器。比如，在一个数据中心里有 10 台服务器，每台服务器就可以用一个唯一的机器 ID 来区分。
     * 取值范围：机器 ID 所占的位数由 machineIdBits 决定，同样设置为 5 位，所以机器 ID 的取值范围也是从 0 到 2^5 - 1，即 0 到 31。在 SnowflakeIdGenerator(1, 1) 中，第二个参数 1 表示当前使用的机器 ID 为 1。
     * 作用
     * 在雪花算法生成的 64 位 ID 中，数据中心 ID 和机器 ID 是其中的一部分，它们确保了在不同数据中心和不同机器上生成的 ID 不会重复。具体来说，数据中心 ID 和机器 ID 会通过左移操作，被放置在生成的 ID 的特定位置，与时间戳和序列号一起组合成一个全局唯一的 ID。
     * 注意事项
     * 在实际使用时，需要确保每个数据中心和每台机器的 ID 是唯一的，否则可能会导致生成的 ID 重复。
     * 可以根据实际的系统规模和需求，调整 dataCenterIdBits 和 machineIdBits 的位数，以平衡数据中心和机器的数量。
     *
     * 使用
     * 单数据中心、少量机器
     * 如果你的系统部署在单个数据中心，并且机器数量较少，你可以手动为每台机器分配唯一的 dataCenterId 和 machineId。由于 dataCenterId 和 machineId 各自占用 5 位，取值范围是 0 - 31，所以在单数据中心场景下，dataCenterId 可以固定为一个值（例如 0），然后为每台机器分配一个唯一的 machineId。
     * // 数据中心 ID 固定为 0，机器 ID 从 0 开始依次递增
     * SnowflakeIdGenerator idGenerator1 = new SnowflakeIdGenerator(0, 0);
     * SnowflakeIdGenerator idGenerator2 = new SnowflakeIdGenerator(0, 1);
     * SnowflakeIdGenerator idGenerator3 = new SnowflakeIdGenerator(0, 2);
     *
     * 多数据中心、少量机器
     * 当系统部署在多个数据中心，且每个数据中心的机器数量较少时，你可以为每个数据中心分配一个唯一的 dataCenterId，然后在每个数据中心内为每台机器分配一个唯一的 machineId。
     *
     * // 数据中心 1 的机器
     * SnowflakeIdGenerator idGenerator1 = new SnowflakeIdGenerator(1, 0);
     * SnowflakeIdGenerator idGenerator2 = new SnowflakeIdGenerator(1, 1);
     *
     * // 数据中心 2 的机器
     * SnowflakeIdGenerator idGenerator3 = new SnowflakeIdGenerator(2, 0);
     * SnowflakeIdGenerator idGenerator4 = new SnowflakeIdGenerator(2, 1);
     * @param dataCenterId
     * @param machineId
     */
    public SnowflakeIdGenerator(long dataCenterId, long machineId) {
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException("Data center ID can't be greater than " + maxDataCenterId + " or less than 0");
        }
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("Machine ID can't be greater than " + maxMachineId + " or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long currentTimestamp = System.currentTimeMillis();

        // 处理时间回拨
        if (currentTimestamp < lastTimestamp) {
            // 可根据实际情况调整等待策略，这里简单抛出异常
            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - currentTimestamp) + " milliseconds");
        }

        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 序列号溢出，等待下一毫秒
                currentTimestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变，重置序列号
            sequence = 0L;
        }

        lastTimestamp = currentTimestamp;

        return ((currentTimestamp - startTimeStamp) << timestampLeftShift) |
                (dataCenterId << dataCenterIdShift) |
                (machineId << machineIdShift) |
                sequence;
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
        for (int i = 0; i < 100000; i++) {
            System.out.println(idGenerator.nextId());
        }
    }
}
